package org.example.diary.exceptions;


import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.example.diary.entity.vo.Response;
import org.example.diary.enums.BizErrorCode;
import org.springframework.dao.DataAccessException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authorization.AuthorizationDeniedException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;


@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public Response exceptionHandler(HttpServletRequest request, Exception ex) {

        try {
            log.info(ex.getMessage());
            if (ex instanceof SystemException) {
                // 自定义异常提示
                SystemException se = (SystemException) ex;
                ex.printStackTrace();
                return Response.error(se.getCode(), se.getMessage());
            }else if (ex instanceof InternalAuthenticationServiceException){
                InternalAuthenticationServiceException serviceException = (InternalAuthenticationServiceException) ex;
                return Response.error(BizErrorCode.USER_NOT_EXISTS.getCode(),serviceException.getMessage());
            }else if (ex instanceof NoResourceFoundException){
                NoResourceFoundException resourceFoundException = (NoResourceFoundException) ex;
                return Response.error(BizErrorCode.ILLEGAL_PARAM.getCode(),resourceFoundException.getMessage());
            }else if (ex instanceof AuthorizationDeniedException){
                AuthorizationDeniedException authorizationDeniedException = (AuthorizationDeniedException) ex;
                return Response.error(BizErrorCode.NO_PERMISSIONS.getCode(),authorizationDeniedException.getMessage());
            }else if (ex instanceof BadCredentialsException){
                BadCredentialsException badCredentialsException = (BadCredentialsException) ex;
                return Response.error(BizErrorCode.LOGIN_FORBID.getCode(), badCredentialsException.getMessage());
            }else if (ex instanceof MethodArgumentNotValidException) {
                //数据验证错误提示
                BizErrorCode errorCode = BizErrorCode.ILLEGAL_PARAM;
                MethodArgumentNotValidException vex = (MethodArgumentNotValidException) ex;
                FieldError fieldError = vex.getBindingResult().getFieldErrors().get(0);
                String msg = String.format("%s, [%s]%s", errorCode.getMessage(), fieldError.getField(), fieldError.getDefaultMessage());
                return Response.error(errorCode.getCode(), msg);

            } else if (ex instanceof TimeoutException) {

                // 请求超时
                return Response.error(BizErrorCode.TIMEOUT);

            } else if (ex instanceof DataAccessException) {

                //数据库操作异常
                return Response.error(BizErrorCode.SQL_ERROR);

            } else if (ex instanceof HttpMessageNotReadableException) {
                String message = ex.getMessage();
                return Response.error(BizErrorCode.ILLEGAL_PARAM);

            } else if (ex instanceof HttpRequestMethodNotSupportedException) {
                Response response = new Response();
                String[] supportedMethods = ((HttpRequestMethodNotSupportedException) ex).getSupportedMethods();
                String method = ((HttpRequestMethodNotSupportedException) ex).getMethod();
                response.setMsg(method + BizErrorCode.REQUEST_NO_SUPPORT.getMessage() + ",只支持:" + Arrays.stream(supportedMethods).collect(Collectors.joining(",")));
                response.setCode(BizErrorCode.REQUEST_NO_SUPPORT.getCode());
                return response;

            } else if (ex instanceof HttpMediaTypeNotSupportedException || ex instanceof HttpMediaTypeNotAcceptableException) {
                return Response.error(BizErrorCode.UNSUPPORTED_MEDIA_TYPE);

            }else {
                return Response.error(BizErrorCode.INTERNAL_ERROR);
            }

        } catch (Exception e) {
            log.error(ex.getMessage(), ex);
            return Response.error(BizErrorCode.INTERNAL_ERROR);
        }

    }

    // 处理请求体校验错误
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Response handleValidationExceptions(MethodArgumentNotValidException ex) {
        log.error(ex.getMessage(), ex);
        BizErrorCode errorCode = BizErrorCode.ILLEGAL_PARAM;
        FieldError fieldError = ex.getBindingResult().getFieldErrors().get(0);
        String[] messages = fieldError.getDefaultMessage().split(": ");
        String message = messages.length == 1 ? messages[0] : messages[messages.length - 1];
        String msg = String.format("%s, [%s] %s", errorCode.getMessage(), fieldError.getField(), message);
        return Response.error(errorCode.getCode(), msg);
    }


    /**
     * 验证统一处理
     *
     * @param ex the ex
     * @return the api error
     */
    @ExceptionHandler({BindException.class})
    @ResponseBody
    public Response ConstraintViolationExceptionHandler(BindException ex) {
        log.error(ex.getMessage(), ex);
        BizErrorCode errorCode = BizErrorCode.ILLEGAL_PARAM;
        FieldError fieldError = ex.getBindingResult().getFieldErrors().get(0);
        String[] messages = fieldError.getDefaultMessage().split(": ");
        String message = messages.length == 1 ? messages[0] : messages[messages.length - 1];
        String msg = String.format("%s, [%s] %s", errorCode.getMessage(), fieldError.getField(), message);
        return Response.error(errorCode.getCode(), msg);
    }

     /**
     * 验证统一处理
     *
     * @param ex the ex
     * @return the api error
     */
    @ExceptionHandler({ConstraintViolationException.class})
    @ResponseBody
    public Response ConstraintViolationExceptionHandler(ConstraintViolationException ex) {
        log.error(ex.getMessage(), ex);
        BizErrorCode errorCode = BizErrorCode.ILLEGAL_PARAM;
        Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
        ConstraintViolation<?> constraintViolation = new ArrayList<>(constraintViolations).get(0);
        String message = constraintViolation.getMessage();
        return Response.error(errorCode.getCode(), message);
    }

}

